<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/jsp/commons/include.jsp"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<title>JavaScript Demo</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<link />
	<style type="text/css">
		#container{
		    width: 760px;
		    height: 600px;
		    margin: 30px auto;
		    /* IE fix 居中布局 */
		    text-align: center;
		}
		 
		#main{
		    width: 760px;
		    height: 600px;
		    margin: 30px auto;
		    /* IE fix 居中布局 */
		 	text-align: left;
		}
	</style>
	
	<script type="text/javascript">
		/*
			Javascript 面向对象编程
			http://coolshell.cn/articles/6441.html
		*/
		var newLine = "<br/>";	
	
		/* 变量定义 */
		var name = 'Chen Hao';
		var email = 'haoel(@)hotmail.com';
		var website = 'http://coolshell.cn';
		
		//====================================================================
		
		/* 用对象来写 */
		var chenhao = {
				name : 'Chen Hao',
				email : 'haoel(@)hotmail.com',
				website : 'http://coolshell.cn'
		};
		
		/* 访问 */
		// 以成员方式
		document.write(chenhao.name + "---" + chenhao.email + "---" + chenhao.website);
		document.write(newLine);
		// 以hash map方式
		document.write(chenhao["name"] + "---" + chenhao["email"] + "---" + chenhao["website"]);
		document.write('<hr/>');
		//====================================================================
		
		document.write("函数 (Javascript对象函数，直接声明，直接赋值，直接用)" + newLine);
		var sayHello = function() {
			var hello = "Hello, I'm " + this.name
				+ ", my email is: " + this.email
				+ ", my website is: " + this.website;
			document.write(hello);
		};
		// 直接赋值
		chenhao.Hello = sayHello;
		chenhao.Hello();
		document.write('<hr/>');
		//====================================================================
		
		/* 规范写法，用function来做class */
		var Person = function(name, email, website) {
			this.name = name;
			this.email = email;
			this.website = website;
			
			this.sayHello = function() {
				var hello = "Hello, I'm " + this.name
					+ ", my email is: " + this.email
					+ ", my website is: " + this.website;
				document.write(hello);
			};
		};
		
		var chenhao = new Person("Chen hao2", "ahoel@hotmail.com", "http://coolshell.com");
		chenhao.sayHello();
		document.write(newLine);
		// 删除对象
		delete chenhao['email'];
		chenhao.sayHello();
		document.write('<hr/>');
		
		//====================================================================
			
		document.write("属性配置-Object.defineProperty" + newLine);
		// 创建对象
		var chenhao = Object.create(null);
		// 设置一个属性
		Object.defineProperty(chenhao,
			'name',{ 	value: 'Chen Hao', 
						writable: true,
						configurable: true,
						enumerable: true
					});
		// 设置多个属性
		Object.defineProperties(chenhao,{
			'email':{ 	value: 'haoel@hotmail.com',
						writable: true,
						configurable: true,
						enumerable: true
					},	
			'website':{	value: 'http://coolshell.cn',
						writable: true,
						configurable: true,
						enumerable: true
					  }
		});
		
		/* Get/Set访问器 */
		var age = 0;
		Object.defineProperty(chenhao,
			'age',{
						get: function() {return age+1;},
						set: function(value) {age = value;},
						configurable: true,
						enumerable: true
					}
		);
		chenhao.age = 100; //调用set
		document.write(chenhao.age);// 调用get 输出101(get中+1了)
		document.write(newLine);
	
		/* 利用已有属性(age)通过get和set构造新的属性(birth_year) */
		Object.defineProperty(chenhao,
			'birth_year',{
								get: function() {
									var d = new Date();
									var y = d.getFullYear();
									return (y - this.age);
								},
								set: function(year) {
									var d = new Date();
									var y = d.getFullYear();
									document.write("Object.defineProperty y:" + y + "\tyear:" + year + newLine);
									this.age = y - year;
								}
							}		
		);
		
		document.write(chenhao.birth_year);
		document.write(newLine);
		chenhao.birth_year = 2000;
		document.write(chenhao.age);
		document.write(newLine);
		
		// ---------------------
		
		var chenhao2 = {
				name: 'Chen Hao',
				email: 'haoel@hotmail.com',
				website: 'http://coolshell.cn',
				age: 100,
				get birth_year() {
					var d = new Date();
					var y = d.getFullYear();
					return (y - this.age);
				},
				set birth_year(year) {
					var d = new Date();
					var y = d.getFullYear();
					this.age = y - year;
				} 
		}
		document.write('<hr/>');
		//====================================================================
		
		document.write("查看对象属性配置" + newLine);
		// 列出对象的属性
		function listProperties(obj) {
			var newLine = "<br/>";
			var names = Object.getOwnPropertyNames(obj);
			for (var i=0; i<names.length; i++) {
				var prop = names[i];
				document.write(prop + newLine);
				
				// 
				var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
				for (var attr in descriptor) {
					document.write("..." + attr + ":" + descriptor[attr]);
					document.write(newLine);
				}
				document.write(newLine);
			}
		}
		listProperties(chenhao);
		
		document.write('<hr/>');
		
		//====================================================================
			
		document.write("call,apply,bind和this" + newLine);
		// this
		function print(text) {
			document.write(this.value + '-' + text + '<br/>');
		}
		
		var a = {value: 10, print: print};
		var b = {value: 20, print: print};
		
		print('hello');	// this=>global, output: "undefined-hello"
		a.print('a');	// this=>a, output: "10-a"
		b.print('b');	// this=>b, output: "20-b"
		a['print']('a');	// this=>a, output: "10-a"
		document.write('<hr/>');
		
		// call apply 性能:call>apply
		print.call(a, 'a');
		print.call(b, 'b');
		
		print.apply(a, ['a']);
		print.apply(b, ['b']);
		document.write('<hr/>');
		
		// 在bind后，this指针，可能会有不一样，因为Javascript是动态的
		var p = print.bind(a);
		p('a');				//this=>a, output "10-a"
		p.call(b, 'b');		//this=>a, output "10-b"
		p.apply(b, ['b']);	//this=>a, output "10-b"
		document.write("<hr/>");
		
		//====================================================================
	
		document.write("继承和重载" + newLine);
		var Person = Object.create(null);
		Object.defineProperties(
			Person, {
				'name': {value: 'Chen Hao', writable: true},
				'email': {value: 'haoel@hotmail.com'},
				'website': {value: 'http://coolshell.cn'}
			}		
		);
		
		Person.sayHello = function() {
			var hello = "<p>Hello, I am " + this.name + ", <br/>" 
						+ "my email is: " + this.email + ", <br/>"
						+ "my website is: " + this.website;
			document.write(hello + newLine);
		};
		
		var Student = Object.create(Person);
		Student.no = "1234567";	// 学号
		Student.dept = "Computer Science"; // 系
		
		// 使用Person属性
		document.write(Student.name + ' ' + Student.email + ' ' + Student.website +'<br>');
		// 使用Person方法
		Student.sayHello();
		// 重载sayHello方法
		Student.sayHello = function (person) {
		    var hello = "<p>Hello, I am "+ this.name  + ", <br>" +
		                "my email is: " + this.email + ", <br>" +
		                "my website is: " + this.website + ", <br>" +
		                "my student no is: " + this. no + ", <br>" +
		                "my departent is: " + this. dept;
		    document.write(hello + '<br>');
		};
		// 再次调用
		Student.sayHello();
		//查看Student的属性（只有 no 、 dept 和 重载了的sayHello）
		document.write('<p>' + Object.keys(Student) + '<br>');
		/*
		当我们的代码需要一个属性的时候，Javascript的引擎会先看当前的这个对象中
		是否有这个属性，如果没有的话，就会查找他的Prototype对象是否有这个属性，
		一直继续下去，直到找到或是直到没有Prototype对象
		*/
		Student.name = 'aaa';
		 
		//输出 FF中是Chen Hao chrome中是aaa
		/*
		如果Prototype有该属性而对象本身没有，ff会在writable为true时为对象创建属性，
		false则不会；而chrome则不论writable为true还是false，都会为对象创建属性
		*/
		document.write('<p>' + Student.name + '</p>');

		//  验证
		document.write("----------------------------------------" + newLine);
		listProperties(Person);
		document.write("----------------------------------------" + newLine);
		listProperties(Student);
		document.write("----------------------------------------" + newLine);
		
		//输出 Chen Hao
		document.write('<p>' +Object.getPrototypeOf(Student).name + '</p>');
		
		//新版的重载SayHello方法
		Student.sayHello = function (person) {
		    Object.getPrototypeOf(this).sayHello.call(this);
		    var hello = "my student no is: " + this. no + ", <br>" +
		                "my departent is: " + this. dept;
		    document.write(hello + '<br>');
		};
		
		//====================================================================
			
		document.write("组合" + newLine);
		
		// target是作用于是对象，source是源对象
		function Composition(target, source) {
		    var desc  = Object.getOwnPropertyDescriptor;
		    var prop  = Object.getOwnPropertyNames;
		    var def_prop = Object.defineProperty;
		 
		    prop(source).forEach(
		        function(key) {
		            def_prop(target, key, desc(source, key))
		        }
		    )
		    return target;
		}
		
		//艺术家
		var Artist = Object.create(null);
		Artist.sing = function() {
		    return this.name + ' starts singing...';
		};
		Artist.paint = function() {
		    return this.name + ' starts painting...';
		};
		 
		//运动员
		var Sporter = Object.create(null);
		Sporter.run = function() {
		    return this.name + ' starts running...';
		};
		Sporter.swim = function() {
		    return this.name + ' starts swimming...';
		};
		 
		Composition(Person, Artist);
		document.write(Person.sing() + '<br>');
		document.write(Person.paint() + '<br>');
		 
		Composition(Person, Sporter);
		document.write(Person.run() + '<br>');
		document.write(Person.swim() + '<br>');
		 
		//看看 Person中有什么？（输出：sayHello,sing,paint,swim,run）
		document.write('<p>' + Object.keys(Person) + '<br>');
		document.write('<hr/>');
		//====================================================================
	</script>
</head>

<body>
	<div id="container">
		<div id="main">
				
		</div>
	</div>
</body>
</html>
